# -*- coding: utf-8 -*-
import json
import logging
from datetime import datetime
from functools import partial

from future.utils import raise_with_traceback

from common.cache import bull_cache
from common.stats import MG_BIGBANG_COLL as mg
from api.transaction.model import *
from common.account.db.account import check_trade_pwd, get_financial_account, get_account
from common.pay.db import (get_pay, create_pay, fill_pay,
                           submit_pay_revert, submit_pay_commit, get_last_pay_by_user_id)
from common.stats.account import get_account_aggr
from common.third.pay import unionagency_v2
from common.transaction.model import WITHDRAW_TYPE
from common.transaction import db
from common.pay.model import PAY_TYPE, PAY_STATUS
from common.utils import exceptions as err
from common.utils.tz import utc_to_local_str
from common.utils.api import page2offset
from common.transaction.model import FIX_WITHDRAW_STATUS
from common.third.pay import justpay, unionagency
from common.utils.exceptions import ParamError
from common.account.model.account import FinancialAccountType
from common.pay.model import PAY_STATUS, CREATED_BY_TYPE
from common.utils import track_logging

_LOGGER = track_logging.getLogger(__name__)

_DEFAULT_PAGE_SIZE = 20


def get_user_transactions(user_id, page, size, transaction_type, start_date):
    limit, offset = page2offset(page, size, default_size=_DEFAULT_PAGE_SIZE)
    transaction_list = []
    transactions = db.get_transactions(
        user_id, limit, offset, transaction_type, start_date)
    for transaction in transactions:
        tran = TransactionLite(transaction.as_dict())
        tran.time = utc_to_local_str(transaction.created_at)
        transaction_list.append(tran)

    return transaction_list


def create_pay_id(user_id, pay_type, created_by=CREATED_BY_TYPE.USER):
    if pay_type not in PAY_TYPE.to_dict():
        raise err.ParamError('parameter invalid')

    pay = create_pay(user_id, pay_type, created_by=created_by)
    return pay.id


def view_pay_status(pay_id):
    pay = get_pay(pay_id)
    if not pay:
        raise err.ParamError('pay id invalid')
    return pay


_HANDLER_DCT = {
    PAY_TYPE.JUSTPAY_WX: partial(justpay.create_charge, service='wechat'),
    PAY_TYPE.JUSTPAY_ALI: partial(justpay.create_charge, service='alipay'),
    PAY_TYPE.JUSTPAY_ALI_QUOTA: partial(justpay.create_charge, service='quota_alipay'),
    PAY_TYPE.JUSTPAY_WX_QUOTA: partial(justpay.create_charge, service='quota_wxpay'),
    PAY_TYPE.JUSTPAY_UNION: partial(justpay.create_charge, service='unionpay'),
    PAY_TYPE.JUSTPAY_QQ: partial(justpay.create_charge, service='qq'),
    PAY_TYPE.JUSTPAY_JD: partial(justpay.create_charge, service='jd'),
    PAY_TYPE.UNIONAGENCY: partial(unionagency.create_charge, service='union'),
    PAY_TYPE.JUSTPAY_TEST: partial(justpay.create_charge, service='test'),
}


def _fill_pay_extra(user):
    extra = {}
    user_id = user.id
    if user.phone:
        user_phone = user.phone[2:] if user.phone.startswith('86') else user.phone
    else:
        user_phone = ''
    try:
        user_stats = mg.user_stats.find_one({'_id': user_id}) or {}
        account__aggr = get_account_aggr(user_id)
        register_date = user_stats.get('register_at')
        account_day = (datetime.now() - register_date).days
        user_info = {
            'user_id': user_id,
            'tel': user_phone,
            'account_day': account_day,
            'active_day': account__aggr.get('active_days', 0),
            'count_recharge': user_stats.get('recharge', {}).get('count', 0),
            'charge_success_count': user_stats.get('recharge', {}).get('count', 0),
            'device_ip': user_stats.get('ip'),
            'total_bet': user_stats.get('pay', {}).get('total', 0),
            'total_recharge': user_stats.get('recharge', {}).get('total', 0),
            'total_withdraw': user_stats.get('withdraw', {}).get('total', 0),
            'count_withdraw': user_stats.get('withdraw', {}).get('count', 0),
            'name': '',
            'bankcard': '',
            'chn': user_stats.get('chn'),
            'user_name': '',
            'user_alicount': '',
        }
        extra.update({'user_info': user_info})
    except Exception as e:
        _LOGGER.warn('_fill_pay_extra error, %s-%s', user_id, e)
    return extra


def submit_pay(user, pay_id, pay_amount, pay_context, sdk_version, channel_id=None):
    pay = submit_pay_commit(pay_id, user.id)

    _LOGGER.info("pay amount at log/transaction.py is %s", pay_amount)

    if not pay:
        raise err.AuthenticateError('payment not access')
    try:
        fill_pay(pay_id, pay_amount, pay_context)
        # do real pay
        pay_type = pay.pay_type
        _LOGGER.info('start pay by %s, pay_id[%s]',
                     PAY_TYPE.get_label(pay_type), pay_id)
        handler = _HANDLER_DCT.get(pay_type)

        if not handler:
            raise err.ParamError('pay type not support')
        extra_info = _fill_pay_extra(user)
        charge = handler(pay, pay_amount, sdk_version, extra_info, channel_id=channel_id)
        _LOGGER.info('end pay by %s, pay_id[%s]',
                     PAY_TYPE.get_label(pay_type), pay_id)
        if not charge:
            submit_pay_revert(pay_id)
        return charge
    except Exception as e:
        submit_pay_revert(pay_id)
        raise_with_traceback(e)


def submit_pay_unionagency_v2(user, pay_id, pay_amount, sdk_version, pay_type, account_num, account_username,
                              account_bank):
    pay = submit_pay_commit(pay_id, user.id)

    _LOGGER.info("pay amount at log/transaction.py is %s", pay_amount)

    if not pay:
        raise err.AuthenticateError('payment not access')
    try:
        fill_pay(pay_id, pay_amount, None)
        # do real pay
        _LOGGER.info('start pay by %s, pay_id[%s]', PAY_TYPE.get_label(pay.pay_type), pay_id)
        extra_info = _fill_pay_extra(user)
        charge = unionagency_v2.create_charge(pay, pay_amount, sdk_version, extra_info, 'union', pay_type, account_num,
                                              account_username, account_bank)

        receiver_info = charge.get('bankcard_info', {})
        extend = {'account_num': pay_amount,
                  'account_holder': account_username,
                  'bank': account_bank,
                  'pay_type': pay_type,
                  'receiver_bank': receiver_info.get('bank'),
                  'receiver_account_holder': receiver_info.get('account_holder'),
                  'receiver_account_number': receiver_info.get('account_number'),
                  'receiver_subbranch': receiver_info.get('subbranch'),
                  }
        pay.extend = json.dumps(extend, ensure_ascii=False)
        pay.save()

        if not charge:
            submit_pay_revert(pay_id)
        return charge
    except Exception as e:
        submit_pay_revert(pay_id)
        raise_with_traceback(e)


def submit_withdraw(user_id, target_type, amount, trade_pwd, target_info):
    if not check_trade_pwd(user_id, trade_pwd):
        raise err.AuthenticateError(u'提现密码错误')
    if bull_cache.check_banker_lock(user_id):
        raise err.LockError(u'牛牛坐庄账户冻结')
    if bull_cache.get_lock_amount(user_id):
        raise err.LockError(u'牛牛账户冻结')
    db.create_withdraw(user_id, target_type, amount, target_info)


def get_withdraw_record(user_id, page, size, fix_status=False):
    limit, offset = page2offset(page, size)
    items = db.get_user_withdraw(user_id, limit, offset)
    resp = []
    for item in items:
        data = WithdrawLite(item.as_dict())
        # 遗留BUG, 客户端和服务端status不一致
        if fix_status:
            data['status'] = FIX_WITHDRAW_STATUS[data['status']]
        data.time = utc_to_local_str(item.created_at)
        data.info = json.loads(item.extend or '{}').get('info', {})
        resp.append(data)
    return resp


def get_last_withdraw_info(user_id, target_type):
    item = db.get_last_info(user_id, target_type)
    if item and item.target_type == WITHDRAW_TYPE.ALIPAY:
        info = json.loads(item.extend).get('info', {})
        return {
            'target_type': item.target_type,
            'info': info
        }
    else:
        # 默认返回支付宝
        return {
            'target_type': WITHDRAW_TYPE.ALIPAY,
        }


def get_last_unionagency_record(user_id):
    """
     "UNIONAGENCY": (1014L, u"内部银行卡代理下分V1"),
    "UNIONAGENCY_V2_ALI": (1015L, u"内部银行卡代理下分V2支付宝"),
    "UNIONAGENCY_V2_BANKCARD": (1016L, u"内部银行卡代理下分V2银行卡"),
    "UNIONAGENCY_V2_WX": (1017L, u"内部银行卡代理下分V2微信"),
    :param user_id:
    :return:
    """
    return get_last_pay_by_user_id(user_id, pay_type=[PAY_TYPE.UNIONAGENCY, PAY_TYPE.UNIONAGENCY_V2_ALI,
                                                      PAY_TYPE.UNIONAGENCY_V2_BANKCARD, PAY_TYPE.UNIONAGENCY_V2_WX])


convert_pay_type = {
    'bankcard': 'bank_card',
    'alipay': 'alipay',
    'wechat': 'wechat'
}


def submit_unionagency_from_third(user_id, pay_amount, account_type):
    """
    注意，用户的提现方式就是默认的充值方式
    :param user_id:
    :param pay_amount:
    :param account_type:
    :return:
    """
    _LOGGER.info("submit_unionagency_from_third {} {} {}".format(user_id, pay_amount, account_type))
    user = get_account(user_id)
    if not user:
        raise ParamError(u'用户不存在')

    account_type = convert_pay_type.get(account_type)
    account_withdraw = get_financial_account(user_id, account_type)
    if not account_withdraw:
        raise ParamError(u'用户该支付方式没有默认卡号')

    account_num = account_withdraw.account
    account_holder = account_withdraw.real_name
    bank = account_withdraw.bank

    if account_type == 'bank_card':
        pay_type = PAY_TYPE.UNIONAGENCY_V2_BANKCARD
    elif account_type == 'alipay':
        pay_type = PAY_TYPE.UNIONAGENCY_V2_ALI
    else:
        pay_type = PAY_TYPE.UNIONAGENCY_V2_WX

    pay = create_pay(user_id, pay_type, created_by=CREATED_BY_TYPE.THIRD)
    pay.price = pay_amount
    pay.status = PAY_STATUS.SUBMIT

    extend = {'account_num': account_num,
              'account_holder': account_holder,
              'bank': bank,
              'pay_type': PAY_TYPE.UNIONAGENCY,
              'from_third': True
              }
    pay.extend = json.dumps(extend, ensure_ascii=False)
    pay.save()

    extra_info = _fill_pay_extra(user)

    return dict(pay_id=pay.id, account_num=account_num, account_holder=account_holder, bank=bank,
                notify_url=settings.UNIONAGENCY_NOTIFY_URL,
                context=json.dumps(extra_info, ensure_ascii=False))
